/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/indexer/impl/IndexUpdateTransactionImpl.java $
* $Id: IndexUpdateTransactionImpl.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.search.indexer.impl;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.store.FSDirectory;
import org.sakaiproject.search.indexer.api.IndexUpdateTransaction;
import org.sakaiproject.search.indexer.api.NoItemsToIndexException;
import org.sakaiproject.search.journal.impl.JournalSettings;
import org.sakaiproject.search.model.SearchBuilderItem;
import org.sakaiproject.search.transaction.api.IndexTransaction;
import org.sakaiproject.search.transaction.api.IndexTransactionException;
import org.sakaiproject.search.transaction.impl.IndexItemsTransactionImpl;
import org.sakaiproject.search.transaction.impl.TransactionManagerImpl;
import org.sakaiproject.search.util.FileUtils;
/**
* A transaction to manage the 2PC of a journaled indexing operation, this is
* created by a Transaction Manager
*
* @author ieb Unit test
* @see org.sakaiproject.search.indexer.impl.test.TransactionalIndexWorkerTest
*/
public class IndexUpdateTransactionImpl extends IndexItemsTransactionImpl implements
IndexUpdateTransaction
{
private static final Log log = LogFactory.getLog(IndexUpdateTransactionImpl.class);
private IndexWriter indexWriter;
private IndexReader indexReader;
private File tempIndex;
private List<SearchBuilderItem> txList;
private SearchBuilderItemSerializer searchBuilderItemSerializer = new SearchBuilderItemSerializer();
private JournalSettings journalSettings;
/**
* @param m
* @param impl
* @throws IndexTransactionException
*/
public IndexUpdateTransactionImpl(TransactionManagerImpl manager, JournalSettings journalSettings,
Map<String, Object> m) throws IndexTransactionException
{
super(manager, m);
this.journalSettings = journalSettings;
}
/**
* @see org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction#addItemIterator()
*/
public Iterator<SearchBuilderItem> lockedItemIterator()
throws IndexTransactionException
{
if (transactionState != IndexTransaction.STATUS_ACTIVE)
{
throw new IndexTransactionException("Transaction is not active ");
}
if (log.isDebugEnabled())
{
log.debug("Tx list on " + this + " is now " + txList);
}
return txList.iterator();
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.transaction.impl.IndexTransactionImpl#doBeforePrepare()
*/
@Override
protected void doBeforePrepare() throws IndexTransactionException
{
try
{
transactionId = manager.getSequence().getNextId();
Document savepointMarker = new Document();
savepointMarker.add(new Field("_txid", String.valueOf(transactionId),
Store.YES, Index.NOT_ANALYZED));
savepointMarker.add(new Field("_txts", String.valueOf(System
.currentTimeMillis()), Store.YES, Index.NOT_ANALYZED));
savepointMarker.add(new Field("_worker", String.valueOf(Thread
.currentThread().getName()), Store.YES, Index.NOT_ANALYZED));
getInternalIndexWriter();
indexWriter.addDocument(savepointMarker);
indexWriter.close();
indexWriter = null;
// save all items
searchBuilderItemSerializer.saveTransactionList(tempIndex, getItems());
}
catch (Exception ex)
{
throw new IndexTransactionException("Failed to prepare transaction", ex);
}
super.doBeforePrepare();
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.transaction.impl.IndexTransactionImpl#doAfterCommit()
*/
@Override
protected void doAfterCommit() throws IndexTransactionException
{
try
{
FileUtils.deleteAll(tempIndex);
}
catch (Exception e)
{
throw new IndexTransactionException("Failed to commit ", e);
}
super.doAfterCommit();
}
/**
* @see org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction#getIndexWriter()
*/
public IndexWriter getIndexWriter() throws IndexTransactionException
{
if (transactionState != IndexTransaction.STATUS_ACTIVE)
{
throw new IndexTransactionException("Transaction is not active ");
}
return getInternalIndexWriter();
}
private IndexWriter getInternalIndexWriter() throws IndexTransactionException
{
if (indexWriter == null)
{
try
{
if (tempIndex == null)
{
tempIndex = ((TransactionIndexManagerImpl) manager)
.getTemporarySegment(transactionId);
}
if (indexReader != null)
{
indexReader.close();
indexReader = null;
}
if (new File(tempIndex,"segments.gen").exists())
{
indexWriter = new IndexWriter(FSDirectory.open(tempIndex),
((TransactionIndexManagerImpl) manager).getAnalyzer(), false, MaxFieldLength.UNLIMITED);
}
else
{
indexWriter = new IndexWriter(FSDirectory.open(tempIndex),
((TransactionIndexManagerImpl) manager).getAnalyzer(), true, MaxFieldLength.UNLIMITED);
}
indexWriter.setUseCompoundFile(true);
// indexWriter.setInfoStream(System.out);
indexWriter.setMaxMergeDocs(journalSettings.getCreateMaxMergeDocs());
indexWriter.setMaxBufferedDocs(journalSettings.getCreateMaxBufferedDocs());
indexWriter.setMergeFactor(journalSettings.getCreateMaxMergeFactor());
}
catch (IOException ex)
{
throw new IndexTransactionException(
"Cant Create Transaction Index working space ", ex);
}
}
return indexWriter;
}
/**
* The name of the temp index should be used to locate the index, and NOT the
* transaction ID.
*
* @see org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction#getTempIndex()
*/
public String getTempIndex()
{
return tempIndex.getAbsolutePath();
}
/**
* The transaction ID will change as the status cahnges. While the
* transaction is active it will have a local ID, when the transaction is
* prepared the cluster wide transaction id will be created. Once prepare
* has been performed, the transaction should be committed
*
* @see org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction#getTransactionId()
*/
public long getTransactionId()
{
return transactionId;
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.transaction.impl.IndexTransactionImpl#doBeforeRollback()
*/
@Override
protected void doBeforeRollback() throws IndexTransactionException
{
try
{
if (indexWriter != null)
{
indexWriter.close();
}
indexWriter = null;
}
catch (Exception ex)
{
throw new IndexTransactionException("Failed to start rollback ", ex);
}
super.doBeforeRollback();
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.transaction.impl.IndexTransactionImpl#doAfterRollback()
*/
@Override
protected void doAfterRollback() throws IndexTransactionException
{
super.doAfterRollback();
try
{
FileUtils.deleteAll(tempIndex);
}
catch (Exception ex)
{
throw new IndexTransactionException("Failed to complete rollback ", ex);
}
tempIndex = null;
}
/**
* @see org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction#setItems(java.util.List)
*/
public void setItems(List<SearchBuilderItem> items) throws IndexTransactionException
{
super.setItems(items);
txList = new ArrayList<SearchBuilderItem>();
for (Iterator<SearchBuilderItem> itxList = items.iterator(); itxList.hasNext();)
{
SearchBuilderItem sbi = itxList.next();
if (sbi != null
&& sbi.isLocked() )
{
String ref = sbi.getName();
if (ref != null)
{
txList.add(sbi);
}
else
{
log.warn("Null Reference presented to Index Queue, ignoring ");
}
}
}
if (txList.size() == 0)
{
log.warn("No Items found to index");
txList = null;
items = null;
throw new NoItemsToIndexException("No Items were found to add to the index");
}
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.indexer.api.IndexUpdateTransaction#getIndexReader()
*/
public IndexReader getIndexReader() throws IndexTransactionException
{
if (transactionState != IndexTransaction.STATUS_ACTIVE)
{
throw new IndexTransactionException("Transaction is not active ");
}
if (indexReader == null)
{
try
{
if (tempIndex == null)
{
tempIndex = ((TransactionIndexManagerImpl) manager)
.getTemporarySegment(transactionId);
}
if (!new File(tempIndex,"segments.gen").exists())
{
getIndexWriter();
}
if (indexWriter != null)
{
indexWriter.close();
indexWriter = null;
}
indexReader = IndexReader.open(FSDirectory.open(tempIndex), false);
}
catch (IOException ex)
{
throw new IndexTransactionException(
"Cant Create Transaction Index working space ", ex);
}
}
return indexReader;
}
}